﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Xml;
using LinFu.IoC.Configuration;
using LinFu.IoC.Interfaces;
using LinFu.IoC;
using System.Linq;
using CoreEx.Common.Extensions;

namespace CoreEx.Wcf.Common.Implementation
{
    /// <summary>
    /// Creates a custom <see cref="DataContractSerializer"/>.
    /// </summary>
    /// <remarks>
    /// The implementation takes care of the following challenges related to the <see cref="DataContractSerializer"/>.
    /// <list type="table">
    /// <item>
    /// <term>Known Types</term>
    /// <description>The <see cref="DataContractSerializer"/> can only serialize types that are "known" to the serializer.
    /// In order to avoid bloating the service interfaces with the <see cref="KnownTypeAttribute"/>, a list of what is considered 
    /// known types is passed to the constructor of the <see cref="DataContractSerializer"/>.    
    /// </description>
    /// </item>
    /// <item>
    /// <term>Circular references</term>
    /// <description>
    /// The <see cref="DataContractSerializer"/> is not by default capable of serializing circular references. 
    /// In order to enable this feature, we need to explicit tell the <see cref="DataContractSerializer"/> to persist object references.    
    /// </description>
    /// </item>
    /// <item>
    /// <term>Interception</term>
    /// <description>
    /// By providing a <see cref="IDataContractSurrogate"/> implementation, it is possible to hook into the serialization process.
    /// For instance, this can be used to modify and object before it gets serialized or deserialized.    
    /// </description>
    /// </item>
    /// </list>
    /// </remarks>
    [Implements(typeof(IOperationBehavior), ServiceName = "CustomSerializerOperationBehavior")]
    public class CustomSerializerOperationBehavior : DataContractSerializerOperationBehavior, IInitialize
    {
        private IServiceContainer _serviceContainer;

        public CustomSerializerOperationBehavior(OperationDescription operation)
            : base(operation)
        {
        }

        public CustomSerializerOperationBehavior(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute)
            : base(operation, dataContractFormatAttribute)
        {
        }

        ///<summary>
        ///Creates an instance of a class that inherits from <see cref="XmlObjectSerializer" /> for serialization and deserialization operations with an <see cref="T:System.Xml.XmlDictionaryString" /> that contains the namespace.
        ///</summary>        
        ///<returns>
        ///An instance of a class that inherits from the <see cref="XmlObjectSerializer" /> class.
        ///</returns>        
        ///<param name="type">The type to serialize or deserialize.</param>
        ///<param name="name">The name of the serialized type.</param>
        ///<param name="ns">An <see cref="XmlDictionaryString" /> that contains the namespace of the serialized type.</param>
        ///<param name="knownTypes">An <see cref="IList{T}" /> of <see cref="Type" /> that contains known types.</param>
        public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
        {

            return new NetDataContractSerializer();
            
            
            //Resolve the known types
            var serviceKnownTypes = _serviceContainer.GetService<IList<Type>>("KnownTypes").Where(t => !t.IsInterface).ToList();

            if (knownTypes != null)
                knownTypes.ForEach(serviceKnownTypes.Add);

            IDataContractSurrogate dataContractSurrogate = null;
            
            //Check to see if the service container contains a IDataContractSurrogate implementation.
            if (_serviceContainer.Contains(typeof(IDataContractSurrogate)))
                dataContractSurrogate = _serviceContainer.GetService<IDataContractSurrogate>();
            

            //Create a custom serializer that preserves parent/child object references and "knows" about types that may be serialized
            var serializer = new DataContractSerializer(type, serviceKnownTypes, int.MaxValue, false,
                                                        true, dataContractSurrogate);

            //var ser = new NetDataContractSerializer();
            //return ser;
        
            return serializer;
        }

        public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
        {
            return new NetDataContractSerializer();
        }

        
        public void Initialize(IServiceContainer source)
        {
            _serviceContainer = source;
        }
    }
}